package com.codeplex.jtfscli.commands;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.codeplex.jtfscli.util.ArgParser;
import com.codeplex.jtfscli.util.SpecFactory;
import com.codeplex.jtfscli.util.TFSConnectionFactory;
import com.codeplex.jtfscli.util.TextTable;
import com.microsoft.tfs.core.TFSTeamProjectCollection;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Changeset;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType;
import com.microsoft.tfs.core.clients.versioncontrol.specs.WorkspaceSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.workspacecache.CachedWorkspace;
import com.microsoft.tfs.core.clients.versioncontrol.workspacecache.WorkspaceCache;

public class HistoryCommand extends Command {

	protected final SpecFactory specFactory;

	public HistoryCommand(final TFSConnectionFactory connectionFactory,
			final SpecFactory specFactory) {
		super(connectionFactory);
		this.specFactory = specFactory;
	}

	public HistoryCommand() {
		super();
		this.specFactory = new SpecFactory();
	}

	public void run(final String[] args, final String usernameAndDomain,
			final String password, final CachedWorkspace cw) throws Exception {
		final Map<String, String> parsedArgs = ArgParser.parse(Arrays
				.asList(args));
		final List<String> regularArgs = ArgParser.findRegularArgs(Arrays
				.asList(args).subList(1, args.length));
		run(parsedArgs, regularArgs, usernameAndDomain, password, cw);
	}

	@Override
	public void run(final Map<String, String> parsedArgs,
			final List<String> regularArgs, String usernameAndDomain,
			String password) throws Exception {
		run(parsedArgs,
				regularArgs,
				usernameAndDomain,
				password,
				WorkspaceCache.load(
						connectionFactory.getDefaultPersistenceStore())
						.findCachedWorkspace(System.getProperty("user.dir")));
	}

	public void run(final Map<String, String> parsedArgs,
			final List<String> regularArgs, final String usernameAndDomain,
			final String password, final CachedWorkspace cw)
			throws ParseException {
		// Load a workspace.
		final TFSTeamProjectCollection connection;
		// Optional server argument.
		if (parsedArgs.containsKey("-server"))
			connection = connectionFactory.createConnection(
					parsedArgs.get("-server"), usernameAndDomain, password);
		else
			// No -server argument so try using current working directory.
			connection = connectionFactory.createConnectionForWorkspace(cw);
		// Optional version argument.
		// Version of the item. I guess this would be useful if you were looking
		// at two items which had the same path or something. Anyway, it is a
		// required argument to queryHistory.
		final VersionSpec version;
		final VersionSpec versionFrom, versionTo;
		if (parsedArgs.containsKey("-version")) {
			final String sVersion = parsedArgs.get("-version");
			// TFS separates versions with the '~' character.
			final int versionSeparatorIndex = sVersion.indexOf('~');
			// Only one version.
			if (versionSeparatorIndex == -1) {
				versionFrom = specFactory.createVersionSpec(sVersion,
						new WorkspaceSpec(cw.getName(), cw.getOwnerName()));
				versionTo = null;
			} else {
				versionFrom = specFactory.createVersionSpec(
						sVersion.substring(0, versionSeparatorIndex),
						new WorkspaceSpec(cw.getName(), cw.getOwnerName()));
				versionTo = specFactory.createVersionSpec(
						sVersion.substring(versionSeparatorIndex + 1),
						new WorkspaceSpec(cw.getName(), cw.getOwnerName()));
			}
			version = versionFrom;
		} else {
			version = specFactory.createVersionSpec("W",
					new WorkspaceSpec(cw.getName(), cw.getOwnerName()));
			versionFrom = versionTo = null;
		}
		final int stopAfter = parsedArgs.containsKey("-stopafter") ? Integer
				.parseInt(parsedArgs.get("-stopafter")) : Integer.MAX_VALUE;
		final String user;
		if (parsedArgs.containsKey("-user"))
			user = parsedArgs.get("-user").equals("*") ? null : parsedArgs
					.get("-user");
		else
			user = null;
		final boolean slotMode = parsedArgs.containsKey("-slotmode");
		final RecursionType recursive = parsedArgs.containsKey("-recursive") ? RecursionType.FULL
				: RecursionType.NONE;
		final boolean isDetailed = parsedArgs.containsKey("-format")
				&& parsedArgs.get("-format").equals("detailed");
		// TFS has problems dealing with dots as the local directory and local
		// directories which are not specified as absolute paths. These issues
		// are handled by createItemspec.
		final String itemSpec = specFactory.createItemspec(regularArgs.get(0),
				recursive == RecursionType.FULL).getItem();
		run(connection, version, versionFrom, versionTo, stopAfter, user,
				slotMode, recursive, isDetailed, itemSpec);
	}

	/**
	 * Prints history of the given item.
	 * 
	 * @param connection
	 *            Connection to use to query history.
	 * @param version
	 *            The version of the item. I'm not entirely sure how this is
	 *            different from versionFrom but my best guess is that perhaps
	 *            two different files could have, at one point, been given the
	 *            same name and located at the same position (obviously not at
	 *            the same time) and this determines which one to track.
	 * @param versionFrom
	 *            Earliest version to retrieve. Null for earliest version
	 *            overall.
	 * @param versionTo
	 *            Latest version to retrieve. Null for latest version overall.
	 * @param stopAfter
	 *            I think this is similar to LIMIT in MySQL.
	 * @param user
	 *            Retrieves only this user's changes (or null for all users).
	 * @param slotMode
	 *            Basically the opposite of version. Version specifies one file
	 *            in one location over its lifetime in that location. slotMode
	 *            specifies all files in that location.
	 * @param recursive
	 * @param isDetailed
	 * @param itemSpec
	 *            The item for which to query history.
	 */
	public void run(final TFSTeamProjectCollection connection,
			final VersionSpec version, final VersionSpec versionFrom,
			final VersionSpec versionTo, final int stopAfter,
			final String user, final boolean slotMode,
			final RecursionType recursive, final boolean isDetailed,
			final String itemSpec) {
		final SimpleDateFormat sdf = new SimpleDateFormat(
				"MMM dd, yyyy hh:mm:ss a");
		final Changeset[] changes = connection.getVersionControlClient()
				.queryHistory(itemSpec, version, 0, recursive, user,
						versionFrom, versionTo, stopAfter, isDetailed,
						slotMode, false, true);
		if (isDetailed)
			printDetailed(changes, sdf);
		else
			printBrief(changes, sdf);
	}

	/**
	 * Prints the change sets in brief format.
	 * 
	 * @param changeSets
	 *            The change sets to print.
	 * @param dateFormat
	 *            The format of the date to use.
	 */
	public static void printBrief(final Changeset[] changeSets,
			final SimpleDateFormat dateFormat) {
		final TextTable tt = new TextTable("Changeset", "User", "Date",
				"Comment");
		for (final Changeset cs : changeSets == null ? new Changeset[] {}
				: changeSets)
			tt.add(String.valueOf(cs.getChangesetID()), cs.getOwner(),
					dateFormat.format(cs.getDate().getTime()), cs.getComment());
		System.out.println(tt);
	}

	/**
	 * Prints the change sets in detailed format.
	 * 
	 * @param changesSets
	 * @param dateFormat
	 */
	public static void printDetailed(final Changeset[] changeSets,
			final SimpleDateFormat dateFormat) {
		for (final Changeset cs : changeSets == null ? new Changeset[] {}
				: changeSets) {
			System.out.println("Changeset: " + cs.getChangesetID());
			System.out.println("User: " + cs.getOwner());
			System.out.println("Date: "
					+ dateFormat.format(cs.getDate().getTime()));
			System.out.println();
			System.out.println("Comment:");
			System.out.println("  " + cs.getComment());
			System.out.println();
			System.out.println("Items:");
			for (final Change c : cs.getChanges()) {
				System.out.print("  " + c.getChangeType().toUIString());
				System.out.println(" " + c.getItem().getServerItem());
			}
			System.out.println();
			System.out.println(StringUtils.leftPad("", 79, '-'));
		}
	}
}
